<?php

namespace Aoe\Emulator\Processor;

use Aoe\Database\Sprite;
use Aoe\Database\SqlBuilder;
use Aoe\Emulator\Ifc\Key;
use Aoe\Emulator\Ifc\Type;
use Aoe\Emulator\Pascal\PascalProvider;
use Aoe\Emulator\Schema\Element\Element;
use Aoe\Util\Exception;
use PDO;

/**
 * # 增删改处理器
 */
class Entity extends Middle
{
    const string ERR_UNIQUE = '${name}数据唯一性验证错误';
    
    /**
     * @param array|null $input 外部输入参数 name => Sprite|value
     *
     * @return int
     * @throws \Exception
     */
    public function insert(?array $input = null): int
    {
        $sprites = [];
        /** @var Element $element */
        foreach ($this->model->getElementIterator() as $element) {
            if ($element->isVirtual()) continue;

            $name  = $element->name;
            $value = $input[$name] ?? null;
            // 处理值为Sprite的情况
            if ($value instanceof Sprite) { $sprites[] = $value; continue; }
            
            $sprite = PascalProvider::get($element)->calc($value, '=', $input);
            if (!$sprite) continue;

            // 唯一性验证
            if ($element->get(Type::UNIQUE, false)) {
                $search = new Search($this->model, $this->builder);
                if ($search->orWhere([$name => $value])->setElements([Type::ID])->count() > 0) {
                    throw new Exception(self::ERR_UNIQUE, ['name' => $name]);
                }
            }

            $sprites[] = $sprite;
        }
        // 植入ID
        if ($this->id !== 0)
            $sprites[] = new Sprite('=', $this->id, PDO::PARAM_INT, Type::ID);
        // __owner__
        if (isset($input[Type::OWNER]))
            $sprites[] = new Sprite('=', $input[Type::OWNER], PDO::PARAM_INT, Type::OWNER);
        // __create_time__
        $sprites[] = new Sprite('=', time(), PDO::PARAM_INT, Type::CREATE);
        
        return $this->builder->add($this->table, $sprites);
    }
    
    /**
     *
     * @param array|Array<array{ operator: string, value?: mixed, name: string }>|null|mixed $input 外部输入参数 name => value | { instructor: string, value: mixed }
     *
     * @return int
     * @throws \Exception
     */
    public function modify(?array $input = null): int
    {
        if (empty($this->id)) $this->setId($input[Type::ID] ?? null);
        if (empty($this->id)) throw new Exception(SqlBuilder::ERR_ID);
        
        $sprites = [];
        /** @var Element $element */
        foreach ($this->model->getElementIterator($this->elements) as $element) {
            if ($element->isVirtual()) continue;

            $name = $element->name;
            // 无修改，不计算
            if (!isset($input[$name])) continue;
            // 处理值为Sprite的情况
            $value = $input[$name];
            if ($value instanceof Sprite) { $sprites[] = $value; continue; }
            // 分析运算符和值
            $operator = null;
            if (is_array($value) && isset($value[Key::OPERATOR])) {
                $operator = $value[Key::OPERATOR];
                $value    = $value[Key::VALUE] ?? null;
            }
            
            $sprite = PascalProvider::get($element)->calc($value, $operator, $input);
            $sprite && ($sprites[] = $sprite);
        }
        // __update_time__
        $sprites[] = new Sprite('=', time(), PDO::PARAM_INT, Type::UPDATE);
        
        return $this->builder->modify($this->table, $sprites, $this->id);
    }
    
    /**
     * @param array|null $input
     *
     * @return int
     * @throws \Exception
     */
    public function remove(?array $input = null): int
    {
        if (empty($this->id)) $this->id = $input[Type::ID] ?? null;
        if (empty($this->id)) throw new Exception(SqlBuilder::ERR_ID);
        
        return $this->builder->delete($this->table, $this->id);
    }
}